Crear una base de datos básica y configurar Entity Framework para acceder a ella.
CREATE TABLE Estudiantes (
Id INTEGER PRIMARY KEY,
Nombre VARCHAR(50)
);
CREATE TABLE Asignaturas (
Id INTEGER PRIMARY KEY,
Nombre VARCHAR(50)
);
CREATE TABLE AsignaturaEstudiante (
Id INTEGER IDENTITY(1,1),
EstudianteId INTEGER ,
AsignaturaId INTEGER ,
PRIMARY KEY (Id),
FOREIGN KEY (EstudianteId) REFERENCES Estudiantes(Id),
FOREIGN KEY (AsignaturaId) REFERENCES Asignaturas(Id)
);
INSERT INTO Estudiantes VALUES(1, 'Fran');
INSERT INTO Estudiantes VALUES(2, 'Maria');
INSERT INTO Estudiantes VALUES(3, 'Clay');
INSERT INTO Asignaturas VALUES(1, 'Matematicas');
INSERT INTO Asignaturas VALUES(2, 'Lengua');
INSERT INTO Asignaturas VALUES(3, 'Educacion fisica');
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(1,1);
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(2,1);
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(3,1);
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(1,2);
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(2,3);
INSERT INTO AsignaturaEstudiante (EstudianteId, AsignaturaId) VALUES(3,3);
Dependiendo de la base de datos que vayamos a utilizar tendremos que importar un paquete u otro:
Si vamos a trabajar con una base de datos SQL Server importaremos el paquete Microsoft.EntityFrameworkCore.SqlServer:
Si vamos a trabajar con una base de datos SQLite importaremos el paquete Microsoft.EntityFrameworkCore.Sqlite:
Para consutlar una tabla solo tenemos que definir primero el contexto con la conexión a la base de datos y la tabla que queremos mapear (definiendo tambien la clase referente a la tabla):
Clase Estudiante:
namespace EFCoreExample
{
public class Estudiante
{
public int Id { get; set; }
public string Nombre { get; set; }
}
}
Contexto:
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample
{
internal class ColegioContext : DbContext
{
public DbSet<Estudiante>? Estudiantes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
string connection = "Data Source=.;" +
"Initial Catalog = Colegio;" +
"Integrated Security = true;" +
"MultipleActiveResultSets = true;";
optionBuilder.UseSqlServer(connection);
}
}
}
En el programa principal creamos la consulta usando el contexto y mostramos los datos de todas las filas con un foreach:
using EFCoreExample;
using (ColegioContext db = new())
{
IQueryable<Estudiante>? estudiantes = db.Estudiantes;
foreach (Estudiante e in estudiantes)
{
Console.WriteLine($"{e.Id}: {e.Nombre}");
}
}
Si estamos usando una base de datos SQLite el contexto sería tal que así:
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample
{
internal class ColegioContext : DbContext
{
public DbSet<Estudiante>? Estudiantes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
string connection = "dbtest.db";
//string connectionALTERNATIVA = Path.Combine(Environment.CurrentDirectory, "dbtest.db");
optionBuilder.UseSqlite($"Filename={connection}");
}
}
}
En este caso solo tenemos que indicar el archivo de la base de datos, para no tener que indicar toda la ruta podemos indicarle al Visual Studio que copie el archivo en el working directory del programa:
Cuando tenemos dos tablas con una relacion N:N en una base de datos relacional se genera una tercera tabla que maneja las relaciones entre las dos tablas, a partir de EF Core 5.0 podemos configurar una relacion many-to-many sin necesidad de mapear la tabla intermedia que se genera en la relacion (en el siguiente apartado se muestra como mapear la tabla intermedia por si queremos hacer algún mapeo con un nombre alternativo de alguna columna)
En este ejemplo tendremos una tabla Estudiantes y una tabla Asignaturas que tienen una relacion N:N entre ellas, por lo tanto se generará una tabla AsignaturaEstudiante.
Antes de nada tenemos que añadir el paquete Microsoft.EntityFrameworkCore.SqlServer para poder manejar conexiones con una base de datos SQL Server:
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.7" />
</ItemGroup>
El código lo añadimos en el archivo del proyecto (.csproj) o tambien podemos añadir el paquete con el NuGet.
Primero creamos el contexto para conectarnos a la base de datos:
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample
{
internal class ColegioContext : DbContext
{
public DbSet<Estudiante>? Estudiantes { get; set; }
public DbSet<Asignatura>? Asignaturas { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
string connection = "Data Source=.;" +
"Initial Catalog = Colegio;" +
"Integrated Security = true;" +
"MultipleActiveResultSets = true;";
optionBuilder.UseSqlServer(connection);
}
}
}
En el contexto indicamos con DbSet las tablas que mapearemos de la base de datos, y en el método OnConfiguring configuramos la conexión con la base de datos.
NOTA: Los nombres que indicamos en las variables de DbSet tienen que coincidir con los nombres de las tablas en la base de datos, en este caso Estudiantes y Asignaturas.
A continuación definimos las clases en las que almacenaremos la información relativa a las tablas de la base de datos:
Clase Asignatura:
namespace EFCoreExample
{
public class Asignatura
{
public int Id { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Estudiante> Estudiante { get; set; }
}
}
Clase Estudiante
namespace EFCoreExample
{
public class Estudiante
{
public int Id { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Asignatura> Asignatura { get; set; }
}
}
NOTA: Al definir las clases definimos una coleccion que será donde se almacene la información referente a la tabla con la que está relacionada, en este caso Estudiante almacenará las asignaturas de dicho estudiante por ejemplo... EF realizará un mapeo automático de la tabla intermedia, y usará el nombre de dicha colección y el nombre de la clave primaria para establecer el nombre de la columna en la tabla intermedia con el que se se tiene que mapear, en este caso en la clase Estudiante por ejemplo, la coleccion se llama Asignatura, y la clave primaria Id por lo tanto se mapeará con la columna AsignaturaId en la tabla intermedia, si la tabla intermedia no tiene una columna con ese nombre EF lanzará una excepción al intentar realizar el mapeo, en el caso de que quisiésemos utilizar algún nombre alternativo tendríamos que realizar el mapeo de la tabla intermedia de forma manual.
Desde la clase principal realizamos la consulta asi:
using EFCoreExample;
using Microsoft.EntityFrameworkCore;
using (ColegioContext db = new())
{
IQueryable<Estudiante>? estudiantes = db.Estudiantes?.Include(e => e.Asignatura);
foreach (Estudiante e in estudiantes)
{
Console.WriteLine(e.Nombre);
Console.WriteLine("Asignaturas:");
foreach (Asignatura item in e.Asignatura)
{
Console.WriteLine("\t" + item.Nombre);
}
}
}
Usamos Include para que se haga una consulta de los datos de la tabla Asignaturas para poder rellenar la información relativa a las asignaturas de cada estudiante.
En el caso anterior si quisiesemos usar como nombre de la coleccion Asignaturas en vez de Asignatura (plural en vez de singular) necesitamos hacer un mapeo manual de la tabla intermedia para poder asignar de manera manual el nombre de las columnas.
La aplicación principal queda igual, solo que cambiamos Asignatura por Asignaturas.
Aplicacion:
using EFCoreExample;
using Microsoft.EntityFrameworkCore;
using (ColegioContext db = new())
{
IQueryable<Estudiante>? estudiantes = db.Estudiantes?.Include(c => c.Asignaturas);
foreach (Estudiante e in estudiantes)
{
Console.WriteLine(e.Nombre);
Console.WriteLine("Asignaturas:");
foreach (Asignatura item in e.Asignaturas)
{
Console.WriteLine("\t" + item.Nombre);
}
}
}
En el contexto declaramos un nuevo DbSet para indicar la tabla intermedia AsignaturaEstudiante.
A mayores definimos la función OnModelCreating para indicar como realizar el mapeo entre la tabla intermedia y sus columnas y las demas tablas:
Contexto:
using Microsoft.EntityFrameworkCore;
namespace EFCoreExample
{
internal class ColegioContext : DbContext
{
public DbSet<Estudiante>? Estudiantes { get; set; }
public DbSet<Asignatura>? Asignaturas { get; set; }
public DbSet<AsignaturaEstudiante>? AsignaturaEstudiante { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionBuilder)
{
string connection = "Data Source=.;" +
"Initial Catalog = Colegio;" +
"Integrated Security = true;" +
"MultipleActiveResultSets = true;";
optionBuilder.UseSqlServer(connection);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Estudiante>()
.HasMany(p => p.Asignaturas)
.WithMany(p => p.Estudiantes)
.UsingEntity<AsignaturaEstudiante>(
j => j
.HasOne(pt => pt.Asignatura)
.WithMany(t => t.AsignaturaEstudiantes)
.HasForeignKey(pt => pt.AsignaturaId),
j => j
.HasOne(pt => pt.Estudiante)
.WithMany(p => p.AsignaturaEstudiantes)
.HasForeignKey(pt => pt.EstudianteId));
}
}
}
En las clases Estudiante y Asignatura añadimos una coleccion para la tabla intermedia.
Estudiante:
namespace EFCoreExample
{
public class Estudiante
{
public int Id { get; set; }
public string Nombre { get; set; }
public ICollection<Asignatura> Asignaturas { get; set; }
public List<AsignaturaEstudiante> AsignaturaEstudiantes { get; set; }
}
}
Asignatura:
namespace EFCoreExample
{
public class Asignatura
{
public int Id { get; set; }
public string Nombre { get; set; }
public virtual ICollection<Estudiante> Estudiantes { get; set; }
public List<AsignaturaEstudiante> AsignaturaEstudiantes { get; set; }
}
}
Creamos una nueva clase AsignaturaEstudiante que almacenará la información de la tabla intermedia.
Hay que tener en cuenta que a mayores de los Ids (que es la información que hay almacenada en la base de datos) estamos creando objetos que alamcenaran la información completa de las tablas que hay relacionadas (Estudiantes y Asignaturas).
AsignaturaEstudiante:
namespace EFCoreExample
{
public class AsignaturaEstudiante
{
public int Id { get; set; }
public int AsignaturaId { get; set; }
public Asignatura Asignatura { get; set; }
public int EstudianteId { get; set; }
public Estudiante Estudiante { get; set; }
}
}
El uso de Entity Framework Core con Azure está documentando en está página.
C# | Entity Framework